#include <seccomp.h>
#include <errno.h>
#include <string.h>

enum {
	PLEDGE_STDIO = 1 << 0,
	PLEDGE_PROC  = 1 << 1,
	PLEDGE_EXEC  = 1 << 2,
};

typedef struct {
	char *name;
	int mask;
} promise;

promise ptable[] = {
	{ .name = "stdio", .mask = PLEDGE_STDIO },
	{ .name = "rpath", .mask = 0 },
	{ .name = "wpath", .mask = 0 },
	{ .name = "cpath", .mask = 0 },
	{ .name = "dpath", .mask = 0 },
	{ .name = "tmppath", .mask = 0 },
	{ .name = "inet", .mask = 0 },
	{ .name = "fattr", .mask = 0 },
	{ .name = "flock", .mask = 0 },
	{ .name = "unix", .mask = 0 },
	{ .name = "dns", .mask = 0 },
	{ .name = "getpw", .mask = 0 },
	{ .name = "sendfd", .mask = 0 },
	{ .name = "recvfd", .mask = 0 },
	{ .name = "ioctl", .mask = 0 },
	{ .name = "tty", .mask = 0 },
	{ .name = "proc", .mask = PLEDGE_PROC },
	{ .name = "exec", .mask = PLEDGE_EXEC },
	{ .name = "prot_exec", .mask = 0 },
	{ .name = "settime", .mask = 0 },
	{ .name = "ps", .mask = 0 },
	{ .name = "vminfo", .mask = 0 },
	{ .name = "id", .mask = 0 },
	{ .name = "pf", .mask = 0 },
	{ .name = "audio", .mask = 0 },
};

int
pledge(const char *promises, const char *paths[])
{
	int i, n, f;
	int flags = 0;

        int rc = -1;
        scmp_filter_ctx ctx;

        ctx = seccomp_init(SCMP_ACT_TRAP);
        if (!ctx) {
		errno = EACCES;
		goto out;
	}

	while (*promises) {
		// skip spaces
		while (*promises && *promises == ' ') promises++;

		// look for a token
		f = 0;
		for (i = 0; i < sizeof(ptable)/sizeof(*ptable); i++) {
			n = strlen(ptable[i].name);
			if (!strncmp(promises, ptable[i].name, n)) {
				// this can be removed once every promise has been implemented
				if (!ptable[i].mask) {
					errno = ENOSYS;
					goto out;
				}
				flags |= ptable[i].mask;
				promises += n;
				f = 1;
				break;
			}
		}

		// what we saw was not any valid token
		if (!f) {
			errno = EINVAL;
			goto out;
		}

		// ensure the token is terminated by a space or end of string
		if (*promises && *promises != ' ') {
			errno = EINVAL;
			goto out;
		}
	}

#define RULE(syscall) \
        rc = seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(syscall), 0); \
        if (rc < 0) goto out

	// everyone is allowed to exit
	RULE(exit_group);

	if (flags & PLEDGE_STDIO) {
		RULE(read);
		RULE(write);
		RULE(open);
		RULE(close);
		RULE(stat);
		RULE(fstat);
		RULE(lstat);
		RULE(poll);
		RULE(lseek);
		RULE(dup);
		RULE(dup2);
	}

	if (flags & PLEDGE_PROC) {
		RULE(fork);
		RULE(vfork);
		RULE(kill);
		RULE(getpriority);
		RULE(setpriority);
		RULE(setrlimit);
		RULE(setpgid);
		RULE(setsid);
	}

	if (flags & PLEDGE_EXEC) {
		RULE(execve);
	}

        rc = seccomp_load(ctx);
        if (rc < 0) goto out;

	rc = 0;

out:
        seccomp_release(ctx);
        return rc;
}
